/* 
 * File:   testHttpServer.cpp
 * Author: silviudog
 *
 * Created on October 27, 2010, 12:03 PM
 */

#include <sys/socket.h>
#include <sys/time.h>
#include <map>
#include "base/CFdEventMonitor-Inl.h"
#include "utils/sigslot.h"
#include "utils/CSocket-Inl.h"
#include <string>
#include <list>
#include <sstream>
#include <iostream>
#include <algorithm>

// -lxnet

using namespace litevent;
using namespace litevent::util;


CFdEventMonitor *evMonitor = NULL;

class CConnectedClient;
void RemoveServerClient();
std::list<CConnectedClient*>clientsArray;

class CConnectedClient :public sigslot::has_slots<>
{

public:

    CConnectedClient(CSocket** m_socket):clientSocket(*m_socket)
    {
        CLogger::sharedLogger()->WriteFormated("###Client: %d created###",clientSocket->getSocket());
        clientSocket->MakeSocketNonBlock();
        mustRemove = false;
        req_number = 0;
        request.reserve(4096);
        keepAlive = false;
    }

    ~CConnectedClient()
    {
        CLogger::sharedLogger()->WriteFormated("###Client: %d released###",clientSocket->getSocket());
        evMonitor->RemoveEventWatcher(clientSocket->getSocket());
        delete clientSocket;
    }
 

    void OnReceiveEvent(int fd, void *ctx, int revents)
    {
        if(revents & (FDEVENT_ERR | FDEVENT_HUP))
        {
            CLogger::sharedLogger()->WriteFormated(" Client: %d error : %d message:%s", clientSocket->getSocket(), revents,strerror(errno));
            DisconnectClient();
        }
        else if(revents & FDEVENT_IN)
        {                        
            /*@todo: catch exceptions here */

            int toRead = clientSocket->countToRead();

            CLogger::sharedLogger()->WriteFormated("Client: %d must read:%d event: %d", clientSocket->getSocket(),toRead, revents);
            
            if(toRead)
            {         
                char buffer[toRead];
                int readBytes = clientSocket->Read(buffer,toRead);
                request.append(buffer,readBytes);

                //wait for the next event
                if(readBytes <toRead)                
                    return;

                if(request.find("\r\n\r\n")!= std::string::npos)
                {               
                    CLogger::sharedLogger()->WriteFormated("Client: % request completed!",clientSocket->getSocket());
                    CLogger::sharedLogger()->WriteFormated("[request]:%s",request.c_str());

                    /*check keep-alive*/
                    std::transform(request.begin(), request.end(), request.begin(),::tolower);
                    if(request.find("connection: keep-alive\r\n")!=std::string::npos)
                        this->keepAlive = true;

                    socketStream.str("");
                    socketStream<<(++req_number);

                    /*body*/
                    std::string bodyString("<html><body><h3>Request number: ");
                    bodyString+=socketStream.str();
                    bodyString+="</h3></body></html>";

                    //header
                    socketStream.str("");
                    socketStream<<bodyString.length();
                    std::string headerString("HTTP/1.0 200 OK\r\n"                                             
                                             "Content-Type: text/html\r\n");
                    if(keepAlive)
                        headerString.append("Connection: Keep-Alive\r\n");
                    else headerString.append("Connection: close\r\n");

                    headerString+="Content-Length: ";
                    headerString+=socketStream.str();
                    headerString+="\r\n\r\n";

                    headerString.append(bodyString);
                    clientSocket->Write(headerString.c_str(),headerString.length());
                   
                    CLogger::sharedLogger()->WriteFormated("[response]:%s",headerString.c_str());
                   
                    if(this->keepAlive == false)
                        DisconnectClient();

                    request.clear();
                }              

            }
            else
            {
                DisconnectClient();
            }
        }


    }

    void DisconnectClient()
    { 
        mustRemove= true;
    }

private:    
    CSocket *clientSocket;
    std::stringstream socketStream;
    std::string request;
    int req_number;
    bool keepAlive;

public:
    bool mustRemove;
};

void RemoveServerClients()
{
    std::list<CConnectedClient*>::iterator itBegin = clientsArray.begin();
    
    while(itBegin != clientsArray.end())
    {
        if((*itBegin)->mustRemove)
        {
            delete *itBegin;
            itBegin = clientsArray.erase(itBegin);           
        }
        else itBegin++;
    }
}

void serverAcceptHandler(int fd, void *ctx, int revents)
{
    CSocket *serverSocket = (CSocket*)ctx;
    CSocket *clientSocket = NULL;
    
    if(revents & (FDEVENT_ERR | FDEVENT_HUP))
    {
        CLogger::sharedLogger()->WriteFormated(" Server: %d error : %d", serverSocket->getSocket(), revents);        
    }
    else if(revents & FDEVENT_IN)
    {        
        for (int loops = 0; loops < 100 && NULL != (clientSocket = serverSocket->Accept()); loops++)
        {
            CConnectedClient *client = new CConnectedClient(&clientSocket);
            clientsArray.push_back(client);
            evMonitor->SetEventWatcher<CConnectedClient>(clientSocket->getSocket(),NULL,FDEVENT_IN,client,&CConnectedClient::OnReceiveEvent);
        }

    }
}


int main(int argc, char** argv)
{
    CLogger::sharedLogger()->openLog("log.txt", true);
    
    evMonitor = new CFdEventMonitor(EnumEventHandlerTypeUnset);

    if (evMonitor->isInitialised() == false)
    {
        CLogger::sharedLogger()->Write("Failed initialise CFdEventMonitor.\n");
        return 1;
    }

    CLogger::sharedLogger()->WriteFormated("Client monitor used method:%s\n", evMonitor->getCurrentMethod());

    CSocket *serverSocket = new CSocket(8080,0);
    serverSocket->MakeSocketNonBlock();
   
    unsigned int port;
    serverSocket->GetLocalPort(port);
    CLogger::sharedLogger()->WriteFormated("Server running port: %d",port);
    CLogger::sharedLogger()->Write("Wait connection...");

    evMonitor->SetEventWatcher(serverSocket->getSocket(),serverAcceptHandler,(void*)serverSocket, FDEVENT_IN);

    while(1)
    {
        RemoveServerClients();
        evMonitor->QuickPoll(1000);
    }
       
    /*free everything to avoid memory leaks in profiler*/

    if(serverSocket)
        delete serverSocket;

    if(evMonitor)
        delete evMonitor;

    delete CLogger::sharedLogger();

    fprintf(stderr, "End\n");
    return 0;
}

